//
//  main.cpp
//  toycube
//
//  Created by vonchenchen on 2021/3/6.
//  Copyright © 2021 vonchenchen. All rights reserved.
//

#include <iostream>
#include <stdlib.h>
#include <cmath>
#include <algorithm>
#include <ToyRenderer.h>
#include <ToyBmp.h>
#include <ToyTexture.h>

using namespace ToyCube;
//ubuntu gcc main.cpp -I../../../ -std=c++11 -lstdc++ -lm

void test(){
    //    Vec<3, float> vec1({1,2,3});
    //    Vec<3, float> vec2({1,2,3});
    //    float ret = DotMutVec(vec1, vec2);
    //    std::cout<<"ret="<<ret<<std::endl;
    //    vec1.Print();
        
//    Vec<3, float> vec1({1,2,3});
//    Vec<3, float> vec = vec1 * 0.5f + vec1 * 1.0f;
//    vec.Print();
//    exit(0);
    
    //    Vec<3, float> vec1({1,2,3});
    //    Vec<3, float> vec2({4,5,6});
    //    Vec<3, float> vec = CrossMut3(vec1, vec2);
    //    vec.Print();
        
    //    Vec<3, float> vec1({3,4,0});
    //    vec1 = Normalize(vec1);
    //    vec1.Print();
        
    //    Mat<3, 3, float> mat({{1, 2, 3},{4, 5, 6}, {7, 8, 9}});
    //    mat.Transpose();
    //    mat.Print();
        
    //    Mat<2, 2, float> mat1({{1,2}, {3,4}});
    //    Mat<2, 2, float> mat2({{4,3}, {2,1}})
    //    mat1.RightMut(mat2);
    //    mat1.Print();
        
//    Mat<4, 4, float> mat({{1, 2, 3, 4}, {5, 6, 7, 8}, {1, 2, 3, 4}, {5, 6, 7, 8}});
//        Vec<4, float> vec({1,2,3, 1});
//        Vec<4, float> vec_new = TransformCoordinate(mat, vec);
//        vec_new.Print();
        
    //    Mat<4, 4, float> mat = RotateMat(0, 0, 1, 3.1415f);
    //    mat.Print();
    //    Vec<4, float> vec({1,0,0,1});
    //    Vec<4, float> vec_new = TransformCoordinate(mat, vec);
    //    //mat.Print();
    //    vec_new.Print();
        
    //    Bitmap bmp(640, 480);
    //    bmp.Fill(0xff000000);
    //    bmp.DrawLine(0, 0, 300, 300, 0xffffffff);
    //    bmp.SaveFile("./test.bmp");
        
//        Mat<4, 4, float> trans = TranslateMat(0, 0, 0);
//        Mat<4, 4, float> camera = CameraMat({0, 1, 0}, {0, 0, 5}, {0, 0, 0});
//        Mat<4, 4, float> proj = PersProjMat(45, 1, 0.1, 50);
        
    //    trans.Print();
    //    camera.Print();
        //proj.Print();
        
    //    Mat<4, 4, float> mvp = proj;
    //    mvp.RightMut(camera);
    //    mvp.RightMut(trans);
    
//    Vec<3, float> z({0, -0.707107, -0.707107});
//    Vec<3, float> up({0, 1, 0});
//    Vec<3, float> vec = CrossMutVec3(z, up);
//    vec = Normalize(vec);
//    vec.Print();
}

struct VertexAttrib { Vec<4, float> pos; Vec<2, float> uv; Vec<3, float> normal;};

void DrawTriangle(){
   //Mat<4, 4, float> rotate = RotateMat(1, 0, 0, 60);
    //rotate.Transpose();
    Mat<4, 4, float> trans = TranslateMat(0, 0, 0);
    
    Mat<4, 4, float> camera = CameraMat({0, 1, 0}, {-3, 4, 5}, {0, 0, 0});
    std::cout<<"cam"<<std::endl;
    camera.Print();
    Mat<4, 4, float> proj = PersProjMat(45, 1, 0.1, 50);
    std::cout<<"proj"<<std::endl;
    proj.Print();
    
    proj.RightMut(camera);
    proj.RightMut(trans);
    //proj.RightMut(rotate);
    
    Mat<4, 4, float> mvp = proj;
    std::cout<<"mvp"<<std::endl;
    mvp.Print();
        
    ToyRenderer renderer;
    renderer.Init(300, 300);

//测试绘制双三角形
    int tri_index = 0;
//绘制三角形
    Vec<4, float> vertexs[] = {
        {1, 0, -2, 1},
        {0, 1, -2, 1},
        {-1, 0, -2, 1}
    };
    //生成一块纹理
    ToyTexture texture(32, 32);
    for(int i=0; i<32; i++){
        for(int j=0; j<32; j++){
            texture.SetPixel(i, j, 0xffff0000);
        }
    }
    ToyTexture texture2(32, 32);
    for(int i=0; i<32; i++){
        for(int j=0; j<32; j++){
            texture2.SetPixel(i, j, 0xff00ff00);
        }
    }
    ToyTexture texture3(32, 32);
    for(int i=0; i<32; i++){
        for(int j=0; j<32; j++){
            texture3.SetPixel(i, j, 0xff0000ff);
        }
    }
    
    const int VARYING_TEXUV = 0;
    renderer.SetVertxShader([&](int index, ShaderContext &output)->Vec<4, float>{

        std::cout<<"vertexs:"<<std::endl;
        vertexs[index].Print();
        Vec<4, float> pos = TransformCoordinate(mvp, vertexs[index]);
        pos.Print();
        return pos;
    });
    renderer.SetFragmentShader([&](ShaderContext &input)->Vec<4, float>{
        
        Vec<4, float> color;
        //根据顶点坐标插值 获取到当前点的uv坐标
        Vec<2, float> vec_uv = input.varying_vec2f[VARYING_TEXUV];
        //使用uv坐标通过插值在纹理上找到uv坐标对应的颜色
        if(tri_index == 0){
            color = texture.Sample2D(vec_uv[0], vec_uv[1]);
        }else if(tri_index == 1){
            //green back
            color = texture2.Sample2D(vec_uv[0], vec_uv[1]);
        }else if(tri_index == 2){
            //green back
            color = texture3.Sample2D(vec_uv[0], vec_uv[1]);
        }
        //返回对应颜色将被设置到位图对应点上
        return color;
    });
    
    renderer.Draw();
    renderer.SaveBmp("./out1.bmp");
    
    tri_index++;
    vertexs[0] = {1, 0, -3, 1};
    vertexs[1] = {0, 1, -3, 1};
    vertexs[2] = {-1, 0, -3, 1};
    renderer.Draw();
    
    tri_index++;
    vertexs[0] = {1, 0.5, 0-3-1, 1};
    vertexs[1] = {0, 0.5, 1-1, 1};
    vertexs[2] = {-1, 0.5, 0-3, 1};
    renderer.Draw();
    
    renderer.DrawLine(50, 150, 0, 0, 0xffffffff);
    
    renderer.SaveBmp("./out2.bmp");
}

void DrawCube(){
    
    Mat<4, 4, float> rotate = RotateMat(0, 1, 0, PI/8);
    Mat<4, 4, float> trans = TranslateMat(0, 0, 0);
    Vec<3, float> eye_pos = {0, 5, 5};
    Mat<4, 4, float> camera = CameraMat({0, 1, 0}, eye_pos, {0, 0, 0});
    std::cout<<"cam"<<std::endl;
    camera.Print();
    Mat<4, 4, float> proj = PersProjMat(45, 1, 0.1, 50);
    std::cout<<"proj"<<std::endl;
    proj.Print();
    
    proj.RightMut(camera);
    proj.RightMut(trans);
    proj.RightMut(rotate);
    
    Mat<4, 4, float> mvp = proj;
    std::cout<<"mvp"<<std::endl;
    mvp.Print();
        
    ToyRenderer renderer;
    renderer.Init(300, 300);

    //绘制立方体 顶点 uv 法线
    VertexAttrib mesh[] = {
        { {  1, -1,  1, 1}, { 0, 0 }, {1, -1,  1}},
        { { -1, -1,  1, 1}, { 0, 0 }, {-1, -1,  1}},
        { { -1,  1,  1, 1}, { 0, 0 }, {-1,  1,  1}},
        { {  1,  1,  1, 1}, { 0, 0 }, {1,  1,  1}},
        { {  1, -1, -1, 1}, { 0, 0 }, {1, -1, -1}},
        { { -1, -1, -1, 1}, { 0, 0 }, {-1, -1, -1}},
        { { -1,  1, -1, 1}, { 0, 0 }, {-1,  1, -1}},
        { {  1,  1, -1, 1}, { 0, 0 }, {1,  1, -1}},
    };
    int vertext_index[][3] = {
        {0, 1, 2}, {2, 3, 0}, //前
        {4, 5, 6}, {6, 7, 4}, //后
        {1, 5, 6}, {6, 2, 1}, //左
        {4, 0, 3}, {3, 7, 4}, //右
        {3, 2, 6}, {6, 7, 3}, //上
        {0, 1, 5}, {5, 4, 0}, //下
    };
    
    int tri_index = 0;
    //生成一块纹理
    ToyTexture texture0(32, 32);
    for(int i=0; i<32; i++){
        for(int j=0; j<32; j++){
            texture0.SetPixel(i, j, 0xff3f0000);
        }
    }
    ToyTexture texture1(32, 32);
    for(int i=0; i<32; i++){
        for(int j=0; j<32; j++){
            texture1.SetPixel(i, j, 0xff003f00);
        }
    }
    ToyTexture texture2(32, 32);
    for(int i=0; i<32; i++){
        for(int j=0; j<32; j++){
            texture2.SetPixel(i, j, 0xff00003f);
        }
    }
    
    ToyTexture texture_rabbit("../../material/haha.bmp");
    texture_rabbit.SaveFile("./out_rabbit.bmp");
    
    //texture0.Sample2D(0.33, 0.36);
    //texture0.SaveFile("./texture.bmp");
    
    struct Light{
        Vec<3, float> pos;
        Vec<3, float> intensity;
    };
    //光源位置与强度
    auto light = Light{{10, 10, 10}, {500, 500, 500}};
    //环境光强度
    Vec<3, float> amb_light_intensity{ 10, 10, 10 };
    
    //定义索引 模拟shder中varing类型的变量 点的着色传入，插值后片元着色传出用于计算每点的颜色相关
    const int VARYING_TEXUV = 0;
    const int VARYING_NORMAL = 1;
    const int VARYING_COORDS = 2;
    
    VertexAttrib vertexAttrib[3];
    renderer.SetVertxShader([&](int index, ShaderContext &output)->Vec<4, float>{

        std::cout<<"vertexs:"<<std::endl;
        vertexAttrib[index].pos.Print();
        Vec<4, float> pos = TransformCoordinate(mvp, vertexAttrib[index].pos);
        
        //传入纹理坐标 插值后用于片元着色器纹理采样
        output.varying_vec2f[VARYING_TEXUV] = vertexAttrib[index].uv;
        //传入顶点坐标 插值后用于片元着色器计算光照点位置
        output.varying_vec4f[VARYING_COORDS] = vertexAttrib[index].pos;
        //传入顶点发法向量
        output.varying_vec3f[VARYING_NORMAL] = vertexAttrib[index].normal;
        
        return pos;
    });
    renderer.SetFragmentShader([&](ShaderContext &input)->Vec<4, float>{
        
        Vec<4, float> color;
        
        //根据顶点坐标插值 获取到当前点的uv坐标
        Vec<2, float> vec_uv = input.varying_vec2f[VARYING_TEXUV];
        //使用uv坐标通过插值在纹理上找到uv坐标对应的颜色
        if(tri_index%3 == 0){
            color = texture_rabbit.Sample2D(vec_uv[0], vec_uv[1]);
        }else if(tri_index%3 == 1){
            color = texture1.Sample2D(vec_uv[0], vec_uv[1]);
        }else if(tri_index%3 == 2){
            color = texture2.Sample2D(vec_uv[0], vec_uv[1]);
        }
        
        //环境光系数
        Vec<3, float> ka = {0.005, 0.005, 0.005};
        //漫反射系数 b g r
        Vec<3, float> kd = {color[0], color[1], color[2]};
        //镜面反射系数
        Vec<3, float> ks = {0.7937, 0.7937, 0.7937};
        //获取当前点的法向量
        Vec<3, float> normal = input.varying_vec3f[VARYING_NORMAL];
        float p = 150;
        //获取当前点的坐标
        Vec<3, float> pos = {input.varying_vec4f[VARYING_COORDS][0],
                                input.varying_vec4f[VARYING_COORDS][1],
                                input.varying_vec4f[VARYING_COORDS][2]};
        //点向光源
        Vec<3, float> vec_light = SubVec(light.pos, pos);
        //点向眼睛
        Vec<3, float> vec_eye = SubVec(eye_pos, pos);
        //计算光线衰减因子
        float attenuation = DotMutVec(vec_light, vec_light);
        //计算环境光
        Vec<3, float> la = CwiseProduct(amb_light_intensity, ka);
        //计算漫反射 光源强度/衰减系数
        Vec<3, float> ld = CwiseProduct((light.intensity/attenuation), kd);
        //使用法线与光线的偏角修正漫反射
        ld = ld * std::max(0.0f, DotMutVec(Normalize(normal), Normalize(vec_light)));
        //计算镜面反射
        Vec<3, float> h = vec_light + vec_eye;
        h = Normalize(h);
        //镜面反射直射光强度
        Vec<3, float> ls = CwiseProduct((light.intensity/attenuation), kd);
        ls = ls * std::pow(std::max(0.0f, DotMutVec(Normalize(normal), h)), p);
        
        Vec<3, float> light_totle;
        light_totle = light_totle + la;
        light_totle = light_totle + ld;
        light_totle = light_totle + ls;
        //返回对应颜色将被设置到位图对应点上
        color = color + light_totle;
        return color;
    });
    
    for(int i=0; i<12; i+=2){
        //根据索引拿到当前三角形的顶点坐标
        vertexAttrib[0] = mesh[vertext_index[i][0]];
        vertexAttrib[1] = mesh[vertext_index[i][1]];
        vertexAttrib[2] = mesh[vertext_index[i][2]];
        //给当前三角形顶点分配纹理坐标
        vertexAttrib[0].uv = {1, 0};
        vertexAttrib[1].uv = {0, 0};
        vertexAttrib[2].uv = {0, 1};
        renderer.Draw();

        vertexAttrib[0] = mesh[vertext_index[i+1][0]];
        vertexAttrib[1] = mesh[vertext_index[i+1][1]];
        vertexAttrib[2] = mesh[vertext_index[i+1][2]];
        vertexAttrib[0].uv = {0, 1};
        vertexAttrib[1].uv = {1, 1};
        vertexAttrib[2].uv = {1, 0};
        renderer.Draw();
        
        tri_index++;
    }
    
    renderer.SaveBmp("./out3.bmp");
}

int main(int argc, const char * argv[]) {
    
    DrawCube();
    
    return 0;
}
